package cn.edu.cdu;

import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;

/**
 *棋板
 */

public class ChessBoard extends JPanel implements ActionListener{
    //格子的个数
    private int height=20;
    private int width=20;
    //格子的间距
    private int space=30;
    //悔棋次数
    private int undoNum=3;
    //黑白棋的数组
    private ArrayList<GoBang> blackChess=new ArrayList<>();
    private ArrayList<GoBang> whiteChess=new ArrayList<>();
    //地图标记的数组
    private Color[][] map=new Color[height+1][width+1];
    /**
     * 标记当前是黑棋还是白棋
     * false：白棋
     * true：黑棋
     */
    private boolean mark=true;
    private boolean gameOver=false;
    //面板各种组件
    private JButton undo=new JButton("悔  棋 "+undoNum);
    private JButton defeat=new JButton("认  输");
    private JButton reset=new JButton("重  置");
    private JLabel nowRound=new JLabel("当 前 回 合");
    private JLabel nowChess=new JLabel("黑子");
    private JLabel myself=new JLabel("执手");
    private JLabel myChess=new JLabel("黑子");
    private JLabel oppNamePro=new JLabel("对手昵称");
    private JLabel oppIpPro=new JLabel("对手IP地址");
    private JLabel oppName=new JLabel("对手",SwingConstants.CENTER);
    private JLabel oppIp=new JLabel("192.168.1.128",SwingConstants.CENTER);
    private JLabel winInfo=new JLabel("");
    //输入输出流
    private DataInputStream din=null;
    private DataOutputStream dou=null;
    //对手信息
    private Opponent opponent;

    /**
     * 棋盘构造函数
     * @param din 输入流
     * @param dou 输出流
     * @param opponent 对手
     */
    public ChessBoard(final DataInputStream din, final DataOutputStream dou, final Opponent opponent) {
        this.din=din;
        this.dou=dou;
        this.opponent=opponent;
        setLayout(null);
        setBounds(0,0,800,700);
        setBackground(Color.ORANGE);
        setTool();
        setMouse();
        new Thread(new Runnable() {
            @Override
            public void run() {
                boolean exit=true;
                while (exit){
                    System.out.print("");
                    try {
                        Thread.sleep(1000);
                        int x=din.readInt();
                        int y=din.readInt();
                        //悔棋
                        /**
                         * 除了正常的接受坐标之外还接受其他的行为标记
                         * -1：悔棋
                         * -2：认输
                         * -3：发送重置请求
                         * -4：同意重置
                         * -5：拒绝重置
                         */
                        if(x==-1&&y==-1){
                            if(!mark){
                                System.out.println("黑子悔棋");
                            }else{
                                System.out.println("白子悔棋");
                            }
                            undoChess();
                            opponent.setUndoNum(opponent.getUndoNum()-1);
                        }else if(x==-2&&y==-2){
                            if(opponent.isChess()){
                                System.out.println("黑子认输");
                                admit("WHITE");
                            }else{
                                System.out.println("白子认输");
                                admit("BLACK");
                            }
                        }else if(x==-3&&y==-3){
                            int n=JOptionPane.showConfirmDialog(ChessBoard.super.getParent(), "你的对手"+opponent.getName()+"正在请求重置棋盘，是否同意？", "重置信息",JOptionPane.YES_NO_OPTION);
                            if(n==0){
                                dou.writeInt(-4);
                                dou.flush();
                                dou.writeInt(-4);
                                dou.flush();
                                System.out.println("重置棋盘...");
                                resetChess();
                            }else if(n==1){
                                dou.writeInt(-5);
                                dou.flush();
                                dou.writeInt(-5);
                                dou.flush();
                            }
                        }else if(x==-4&&y==-4){
                            JOptionPane.showMessageDialog(ChessBoard.super.getParent(),"对手同意，正在重置!");
                            System.out.println("重置棋盘...");
                            resetChess();
                        }else if(x==-5&&y==-5){
                            JOptionPane.showMessageDialog(ChessBoard.super.getParent(),"对手拒绝重置!","重置信息",JOptionPane.WARNING_MESSAGE);
                        }else{
                            System.out.print("接收到X:"+x+"; Y:"+y+"; ");
                            paintChess(x,y);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        exit=false;
                        connectFail();
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        this.addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                //自己的回合
                if(opponent.isChess()!=mark&&!gameOver&&
                        getCursor()==Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)){
                    //在棋盘范围内
                    if (e.getX()>=15&&e.getX()<=645&&e.getY()>15&&e.getY()<=645){
                        try {
                            dou.writeInt(e.getX());
                            dou.flush();
                            dou.writeInt(e.getY());
                            dou.flush();
                            System.out.print("发送X:"+e.getX()+"; Y:"+e.getY()+"; ");
                            //画出棋子
                            paintChess(e.getX(),e.getY());
                            setCursor(Cursor.getDefaultCursor());
                        } catch (IOException e1) {
                            e1.printStackTrace();
                            connectFail();
                        }
                    }else{
                        System.out.println("NULL");
                    }
                }
            }
        });
    }

    /**
     * 转变鼠标类型
     */
    public void setMouse(){
        this.addMouseMotionListener(new MouseInputAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                super.mouseMoved(e);
                if(opponent.isChess()!=mark&&!gameOver){
                    if(e.getX()>=15&&e.getX()<=645&&e.getY()>15&&e.getY()<=645){
                        if(((e.getX()-30)%space<10||(e.getX()-30)%space>20)&&
                                ((e.getY()-30)%space<10||(e.getY()-30)%space>20)){
                            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                        }else{
                            setCursor(Cursor.getDefaultCursor());
                        }
                    }
                }
            }
        });
    }

    /**
     * 设置面板工具
     */
    public void setTool(){
        //悔棋
        undo.setBounds(660,460,100,40);
        undo.setFont(new Font("微软雅黑",1,17));
        undo.setForeground(new Color(42, 167, 194));
        undo.setBackground(new Color(74, 69, 69));
        undo.setFocusPainted(false);
        undo.addActionListener(this);
        //认输
        defeat.setBounds(660,520,100,40);
        defeat.setFont(new Font("微软雅黑",1,17));
        defeat.setForeground(new Color(42, 167, 194));
        defeat.setBackground(new Color(74, 69, 69));
        defeat.setFocusPainted(false);
        defeat.addActionListener(this);
        //重置
        reset.setBounds(660,580,100,40);
        reset.setFont(new Font("微软雅黑",1,17));
        reset.setForeground(new Color(42, 167, 194));
        reset.setBackground(new Color(74, 69, 69));
        reset.setFocusPainted(false);
        reset.addActionListener(this);
        reset.setVisible(false);
        //当前回合
        nowRound.setBounds(660,25,120,30);
        nowRound.setFont(new Font("微软雅黑",1,20));
        nowRound.setForeground(new Color(72, 121, 161));
        //当前棋子
        nowChess.setBounds(705,67,80,20);
        nowChess.setFont(new Font("楷体",1,20));
        //执手
        myself.setBounds(690,100,80,30);
        myself.setFont(new Font("微软雅黑",1,20));
        myself.setForeground(new Color(120, 37, 161));
        //执手棋子
        myChess.setBounds(705,142,80,20);
        myChess.setFont(new Font("楷体",1,20));
        //对手昵称提示
        oppNamePro.setBounds(670,220,120,30);
        oppNamePro.setFont(new Font("微软雅黑",1,20));
        //对手昵称
        oppName.setText(opponent.getName());
        oppName.setBounds(650,255,120,25);
        oppName.setFont(new Font("楷体",1,20));
        oppName.setForeground(new Color(225, 70, 33));
        //对手Ip提示
        oppIpPro.setBounds(663,290,120,30);
        oppIpPro.setFont(new Font("微软雅黑",1,20));
        //对手Ip
        oppIp.setText(opponent.getIp());
        oppIp.setBounds(645,325,140,25);
        oppIp.setFont(new Font("楷体",1,17));
        oppIp.setForeground(new Color(225, 70, 33));
        //胜利者信息
        winInfo.setBounds(650,380,150,30);
        winInfo.setFont(new Font("楷体",1,27));
        winInfo.setForeground(new Color(255, 8, 4));
        this.add(undo);
        this.add(defeat);
        this.add(reset);
        this.add(nowRound);
        this.add(nowChess);
        this.add(myself);
        this.add(myChess);
        this.add(oppName);
        this.add(oppNamePro);
        this.add(oppIpPro);
        this.add(oppIp);
        this.add(winInfo);
    }

    /**
     * 画出棋子
     * @param X x坐标
     * @param Y y坐标
     */
    private void paintChess(int X,int Y){
        int x=Math.round(((float)X-30)/space);
        int y=Math.round(((float)Y-30)/space);
        if(map[x][y]==null){
            if(mark){
                GoBang gobang=new GoBang(Color.BLACK,x*space+30,y*space+30);
                blackChess.add(gobang);
                map[x][y]=Color.BLACK;
                mark=false;
                repaint();
                System.out.println("BLACK");
            }else{
                GoBang gobang=new GoBang(Color.WHITE,x*space+30,y*space+30);
                whiteChess.add(gobang);
                map[x][y]=Color.WHITE;
                mark=true;
                repaint();
                System.out.println("WHITE");
            }
            String win=isWIn();
            if(win.equals("BLACK")){
                winAfter("BLACK");
            }else if(win.equals("WHITE")){
                winAfter("WHITE");
            }
        }else{
            System.out.println("NULL");
        }
    }

    /**
     * 判断游戏是否结束
     * @return 胜利的棋子颜色，若没有则返回空字符串
     */
    public String isWIn(){
        int x=0,y=0;
        for(int i=0;i<height+1;i++){
            for(int j=0;j<width+1;j++){
                if(map[i][j]!=null){
                    for(int k=0;k<4;k++){
                        y=i;
                        x=j;
                        switch (k){
                            //向右
                            case 0:{
                                int t=0;
                                int mark=0;
                                for(x=j;x<j+5;x++){
                                    if(x==width+1){
                                        mark=1;
                                        break;
                                    }
                                    if(map[y][x]==Color.BLACK){
                                        t++;
                                    }else if(map[y][x]==Color.WHITE){
                                        t+=0;
                                    }else if(map[y][x]==null){
                                        mark=1;
                                        break;
                                    }
                                }
                                if(mark==0&&t==5){
                                    return "BLACK";
                                }else if(mark==0&&t==0){
                                    return "WHITE";
                                }
                                break;
                            }
                            //向下
                            case 1:{
                                int t=0;
                                int mark=0;
                                for(y=i;y<i+5;y++){
                                    if(y==height+1){
                                        mark=1;
                                        break;
                                    }
                                    if(map[y][x]==Color.BLACK){
                                        t++;
                                    }else if(map[y][x]==Color.WHITE){
                                        t=t+0;
                                    }else if(map[y][x]==null){
                                        mark=1;
                                        break;
                                    }
                                }
                                if(mark==0&&t==5){
                                    return "BLACK";
                                }else if(mark==0&&t==0){
                                    return "WHITE";
                                }
                                break;
                            }
                            //往左下
                            case 2:{
                                int t=0;
                                int mark=0;
                                for(x=j;x>j-5;x--){
                                    y=i+(j-x);
                                    if(y==height+1||x==-1){
                                        mark=1;
                                        break;
                                    }
                                    if(map[y][x]==Color.BLACK){
                                        t=t+1;
                                    }else if(map[y][x]==Color.WHITE){
                                        t+=0;
                                    }else if(map[y][x]==null){
                                        mark=1;
                                        break;
                                    }
                                }
                                if(mark==0&&t==5){
                                    return "BLACK";
                                }else if(mark==0&&t==0){
                                    return "WHITE";
                                }
                                break;
                            }
                            //向右下
                            case 3:{
                                int t=0;
                                int mark=0;
                                for(x=j;x<j+5;x++){
                                    y=i+(x-j);
                                    if(y==height+1||x==width+1){
                                        mark=1;
                                        break;
                                    }
                                    if(map[y][x]==Color.BLACK){
                                        t=t+1;
                                    }else if(map[y][x]==Color.WHITE){
                                        t=t+0;
                                    }else if(map[y][x]==null){
                                        mark=1;
                                        break;
                                    }
                                }
                                if(mark==0&&t==5){
                                    return "BLACK";
                                }else if(mark==0&&t==0){
                                    return "WHITE";
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }

        return "";
    }

    /**
     * 胜利之后执行的操作
     * @param color 赢的棋子颜色
     */
    public void winAfter(String color){
        String s="";
        if(color.equals("BLACK")){
            s="黑子";
        }else if(color.equals("WHITE")){
            s="白子";
        }
        System.out.println(s+"胜利！！！");
        JOptionPane.showMessageDialog(this,s+"胜利！");
        winInfo.setText(s+"获胜！");
        undo.setVisible(false);
        defeat.setVisible(false);
        reset.setVisible(true);
        gameOver=true;
    }

    /**
     * 悔棋
     */
    public void undoChess(){
        String s="";
        //对方为黑子
        if(!mark){
            s="黑子";
            map[(blackChess.get(blackChess.size()-1).getX()-30)/space][(blackChess.get(blackChess.size()-1).getY()-30)/space]=null;
            blackChess.remove(blackChess.size()-1);
        }else{
            s="白子";
            map[(whiteChess.get(whiteChess.size()-1).getX()-30)/space][(whiteChess.get(whiteChess.size()-1).getY()-30)/space]=null;
            whiteChess.remove(whiteChess.size()-1);
        }
        JOptionPane.showMessageDialog(this,s+"悔棋!","悔棋信息",JOptionPane.WARNING_MESSAGE);
        mark=!mark;
        repaint();
    }

    /**
     * 断线处理
     */
    public void connectFail(){
        JOptionPane.showMessageDialog(this,"对方已掉线!   程序将退出至初始界面","来自服务器的消息",JOptionPane.ERROR_MESSAGE);
        CardLayout layout= (CardLayout) this.getParent().getLayout();
        layout.show(this.getParent(),"欢迎");
    }

    /**
     * 认输
     */
    public void admit(String color){
        if(color.equals("BLACK")){
            JOptionPane.showMessageDialog(this,"白子认输！ 游戏结束","认输信息",JOptionPane.WARNING_MESSAGE);
        }else{
            JOptionPane.showMessageDialog(this,"黑子认输！ 游戏结束","认输信息",JOptionPane.WARNING_MESSAGE);
        }
        winAfter(color);
    }

    /**
     * 重置棋盘
     */

    public void resetChess(){
        for(int i=0;i<height+1;i++){
            for(int j=0;j<width+1;j++){
                map[i][j]=null;
            }
        }
        undoNum=3;
        blackChess.clear();
        whiteChess.clear();
        gameOver=false;
        winInfo.setText("");
        mark=true;
        opponent.setChess(!opponent.isChess());
        undo.setText("悔  棋 "+undoNum);
        undo.setVisible(true);
        defeat.setVisible(true);
        reset.setVisible(false);
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        //画出棋盘地图
        g.setColor(Color.BLACK);
        g.drawRect(30,30,600,600);
        for(int i=1;i<width;i++){
            g.setColor(Color.BLACK);
            g.drawLine(30+space*i,30,30+space*i,30+height*space);
        }
        for(int i=1;i<height;i++){
            g.setColor(Color.BLACK);
            g.drawLine(30,30+space*i,30+height*space,30+space*i);
        }
        //画出黑棋
        for (int i = 0; i <blackChess.size() ; i++) {
            blackChess.get(i).draw(g);
            if(i==blackChess.size()-1&&mark==false){
                blackChess.get(i).mark(g);
            }
        }
        //画出白棋
        for (int i = 0; i <whiteChess.size() ; i++) {
            whiteChess.get(i).draw(g);
            if(i==whiteChess.size()-1&&mark==true){
                whiteChess.get(i).mark(g);
            }
        }
        //右上角区域边框
        Graphics2D g2= (Graphics2D) g;
        g2.setStroke(new BasicStroke(2.0f));
        g2.setColor(Color.BLACK);
        g2.drawRect(650,20,120,160);
        //右中横线
        g2.drawLine(650,285,780,285);
        //当前棋子
        if(mark==true){
            g.setColor(Color.BLACK);
            nowChess.setText("黑子");
        }else{
            g.setColor(Color.WHITE);
            nowChess.setText("白子");
        }
        g.fillOval(670,65,24,24);
        //执手棋子
        if(opponent.isChess()){
            g.setColor(Color.WHITE);
            myChess.setText("白子");
        }else{
            g.setColor(Color.BLACK);
            myChess.setText("黑子");
        }
        g.fillOval(670,140,24,24);

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==undo){
//            if(opponent.isChess()!=mark&& whiteChess.size()!=0&&blackChess.size()!=0&&undoNum>0){
              if(opponent.isChess()==mark&&undoNum>0&&whiteChess.size()!=0&&blackChess.size()!=0){
                try {
                    dou.writeInt(-1);
                    dou.flush();
                    dou.writeInt(-1);
                    dou.flush();
                    undoChess();
                    undoNum--;
                    undo.setText("悔  棋 "+undoNum);
                } catch (IOException e1) {
                    e1.printStackTrace();
                    connectFail();
                }
            }else{
                JOptionPane.showMessageDialog(this,"无法悔棋!","悔棋信息",JOptionPane.WARNING_MESSAGE);
            }
        }else if(e.getSource()==defeat){
            try {
                dou.writeInt(-2);
                dou.flush();
                dou.writeInt(-2);
                dou.flush();
                if(opponent.isChess()){
                    admit("BLACK");
                }else{
                    admit("WHITE");
                }
            } catch (IOException e1) {
                e1.printStackTrace();
                connectFail();
            }
        }else if(e.getSource()==reset){
            try {
                dou.writeInt(-3);
                dou.flush();
                dou.writeInt(-3);
                dou.flush();
                JOptionPane.showMessageDialog(this,"正在向对手"+opponent.getName()+"发送重置信息...");
            } catch (IOException e1) {
                e1.printStackTrace();
                connectFail();
            }
        }
    }

    public static void main(String[] args) {
        DataInputStream din=null;
        DataOutputStream dou=null;
        JFrame jf=new JFrame();
        jf.setTitle("五子棋");
        jf.setResizable(false);
        jf.setSize(800,700);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jf.setVisible(true);
        jf.add(new ChessBoard(din,dou,new Opponent("你说啥","192.168.11.125",true)));
    }
}


